package com.postgresql.geotools;

import org.apache.commons.lang3.ArrayUtils;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.GmlObjectStore;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.store.ContentDataStore;
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.jdbc.ConnectionLifecycleListener;
import org.geotools.jdbc.JDBCDataStore;
import org.geotools.util.factory.Hints;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.PropertyIsLessThanOrEqualTo;
import org.opengis.filter.expression.Literal;
import org.opengis.filter.identity.GmlObjectId;

import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;

/**
 * @author: 顾志杰
 */
public class GeoTable extends ContentDataStore implements GmlObjectStore {
    protected List<ConnectionLifecycleListener> connectionLifecycleListeners = new CopyOnWriteArrayList();
    protected String databaseSchema;

    JDBCDataStore jdbcDataStore = connnection2mysql();

    public  JDBCDataStore connnection2mysql() {
        JDBCDataStore ds = null;
        DataStore dataStore = null;
        //连接数据库参数
        java.util.Map params = new java.util.HashMap();
        params.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");
        params.put(PostgisNGDataStoreFactory.HOST.key, "192.168.24.203");
        params.put(PostgisNGDataStoreFactory.PORT.key, "5432");
        params.put(PostgisNGDataStoreFactory.DATABASE.key, "import-text");
        params.put(PostgisNGDataStoreFactory.USER.key, "postgres");
        params.put(PostgisNGDataStoreFactory.PASSWD.key, "postgres");
        try {
            dataStore = DataStoreFinder.getDataStore(params);
            if (dataStore != null) {
                ds = (JDBCDataStore) dataStore;
                System.out.println("连接成功");
            } else {

                System.out.println("连接失败");
            }

        } catch (IOException e) {

            e.printStackTrace();

        }

        return ds;
    }

    @Override
    public Object getGmlObject(GmlObjectId gmlObjectId, Hints hints) throws IOException {
        return null;
    }

    @Override
    protected List<Name> createTypeNames() throws IOException {
        return null;
    }

    @Override
    protected ContentFeatureSource createFeatureSource(ContentEntry contentEntry) throws IOException {
        return null;
    }


    @Override
    public void createSchema(SimpleFeatureType featureType) throws IOException {
        if (featureType.getName() == null) {
            String msg = "Schema '" + featureType.getName() + "' already exists";
            throw new IllegalArgumentException(msg);
        } else {
            Connection cx = this.createConnection();

            try {
                String sql = this.createTableSQL(featureType, cx);
                this.LOGGER.log(Level.FINE, "Create schema: {0}", sql);
                Statement st = cx.createStatement();

                try {
                    st.execute(sql);
                } finally {
                    jdbcDataStore.closeSafe(st);
                }

                jdbcDataStore.dialect.postCreateTable(this.databaseSchema, featureType, cx);
            } catch (Exception var14) {
                String msg = "Error occurred creating table";
                throw (IOException)(new IOException(msg)).initCause(var14);
            } finally {
                jdbcDataStore.closeSafe(cx);
            }

        }
    }


    protected final Connection createConnection() {
        try {
            this.LOGGER.fine("CREATE CONNECTION");
            Connection cx = jdbcDataStore.getDataSource().getConnection();
            jdbcDataStore.dialect.initializeConnection((Connection)cx);
            if (this.connectionLifecycleListeners.size() > 0) {
                List<ConnectionLifecycleListener> locals = new ArrayList(this.connectionLifecycleListeners);
                cx = new LifecycleConnection(jdbcDataStore, (Connection)cx, locals);
            }

            return (Connection)cx;
        } catch (SQLException var3) {
            throw new RuntimeException("Unable to obtain connection: " + var3.getMessage(), var3);
        }
    }



    protected String createTableSQL(SimpleFeatureType featureType, Connection cx) throws Exception {
        String[] columnNames = new String[featureType.getAttributeCount()+1];
        String[] sqlTypeNames = null;
        Class[] classes = new Class[featureType.getAttributeCount()+1];
        boolean[] nillable = new boolean[featureType.getAttributeCount()+1];

        int i;
        for(i = 0; i < featureType.getAttributeCount()+1; ++i) {
            if(i == featureType.getAttributeCount()){
                columnNames[i] = "testId";
                classes[i] = String.class;
                nillable[i] = true;
            }else{
                AttributeDescriptor attributeType = featureType.getDescriptor(i);
                columnNames[i] = attributeType.getLocalName();
                classes[i] = attributeType.getType().getBinding();
                nillable[i] = attributeType.getMinOccurs() <= 0 || attributeType.isNillable();
            }

        }

        sqlTypeNames = this.getSQLTypeNames(featureType.getAttributeDescriptors(), cx);
        String[] sqlTypes = new String[sqlTypeNames.length+1];
        for (int i1 = 0; i1 < sqlTypes.length; i1++) {
           if(i1 == sqlTypeNames.length){
               sqlTypes[i1] = "VARCHAR";
           }else{
               sqlTypes[i1] = sqlTypeNames[i1];
           }

        }

        for(i = 0; i < sqlTypeNames.length; ++i) {
            if (sqlTypeNames[i] == null) {
                String msg = "Unable to map " + columnNames[i] + "( " + classes[i].getName() + ")";
                throw new RuntimeException(msg);
            }
        }

        return this.createTableSQL(featureType.getTypeName(), columnNames, sqlTypes, nillable, this.findPrimaryKeyColumnName(featureType), featureType);
    }

    protected String findPrimaryKeyColumnName(SimpleFeatureType featureType) {
        String[] suffix = new String[]{"", "_1", "_2"};
        String[] base = new String[]{"fid", "id", "gt_id", "ogc_fid"};
        String[] var4 = base;
        int var5 = base.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String b = var4[var6];
            String[] var8 = suffix;
            int var9 = suffix.length;

            for(int var10 = 0; var10 < var9; ++var10) {
                String s = var8[var10];
                String name = b + s;
                Iterator var13 = featureType.getAttributeDescriptors().iterator();

                AttributeDescriptor ad;
                do {
                    if (!var13.hasNext()) {
                        return name;
                    }

                    ad = (AttributeDescriptor)var13.next();
                } while(!ad.getLocalName().equalsIgnoreCase(name));
            }
        }

        return "fid";
    }

    private String createTableSQL(String tableName, String[] columnNames, String[] sqlTypeNames, boolean[] nillable, String pkeyColumn, SimpleFeatureType featureType) throws SQLException {
        StringBuffer sql = new StringBuffer();
        jdbcDataStore.dialect.encodeCreateTable(sql);
        jdbcDataStore.encodeTableName(tableName, sql, (Hints)null);
        sql.append(" ( ");
        if (pkeyColumn != null) {
            jdbcDataStore.dialect.encodePrimaryKey(pkeyColumn, sql);
            sql.append(", ");
        }

        for(int i = 0; i < columnNames.length; ++i) {
            jdbcDataStore.dialect.encodeColumnName((String)null, columnNames[i], sql);
            sql.append(" ");
            int length = -1;
            AttributeDescriptor att;
            if (sqlTypeNames[i].toUpperCase().startsWith("VARCHAR") && featureType != null) {
                att = featureType.getDescriptor(columnNames[i]);
                if(att == null){
                    length = 254;
                }else{
                    length = this.findVarcharColumnLength(att);
                }

            }

            if (length == -1) {
                jdbcDataStore.dialect.encodeColumnType(sqlTypeNames[i], sql);
            } else {
                jdbcDataStore.dialect.encodeColumnType(sqlTypeNames[i] + "(" + length + ")", sql);
            }

            if (nillable != null && !nillable[i]) {
                sql.append(" NOT NULL ");
            }

            if (featureType != null) {
                att = featureType.getDescriptor(columnNames[i]);
                jdbcDataStore.dialect.encodePostColumnCreateTable(att, sql);
            }

            if (i < sqlTypeNames.length - 1) {
                sql.append(", ");
            }
        }

        sql.append(" ) ");
        jdbcDataStore.dialect.encodePostCreateTable(tableName, sql);
        return sql.toString();
    }

    private Integer findVarcharColumnLength(AttributeDescriptor att) {
        Iterator var2 = att.getType().getRestrictions().iterator();

        while(var2.hasNext()) {
            Filter r = (Filter)var2.next();
            if (r instanceof PropertyIsLessThanOrEqualTo) {
                PropertyIsLessThanOrEqualTo c = (PropertyIsLessThanOrEqualTo)r;
                if (c.getExpression1() instanceof org.opengis.filter.expression.Function && ((org.opengis.filter.expression.Function)c.getExpression1()).getName().toLowerCase().endsWith("length") && c.getExpression2() instanceof Literal) {
                    Integer length = (Integer)c.getExpression2().evaluate((Object)null, Integer.class);
                    if (length != null) {
                        return length;
                    }
                }
            }
        }

        return jdbcDataStore.dialect.getDefaultVarcharSize();
    }

    private String[] getSQLTypeNames(List<AttributeDescriptor> descriptors, Connection cx) throws SQLException {
        int[] sqlTypes = new int[descriptors.size()];
        String[] sqlTypeNames = new String[sqlTypes.length];

        String sqlTypeName;
        for(int i = 0; i < descriptors.size(); ++i) {
            AttributeDescriptor ad = (AttributeDescriptor)descriptors.get(i);
            Class clazz = ad.getType().getBinding();
            Integer sqlType = jdbcDataStore.dialect.getSQLType(ad);
            if (sqlType == null) {
                sqlType = jdbcDataStore.getMapping(clazz);
            }

            if (sqlType == null) {
                this.LOGGER.warning("No sql type mapping for: " + ad.getLocalName() + " of type " + clazz);
                sqlType = 1111;
            }

            sqlTypes[i] = sqlType;
            if (Geometry.class.isAssignableFrom(clazz)) {
                sqlTypeName = jdbcDataStore.dialect.getGeometryTypeName(sqlType);
                if (sqlTypeName != null) {
                    sqlTypeNames[i] = sqlTypeName;
                }
            }

            sqlTypeName = (String)jdbcDataStore.getDBsqlTypesCache().get(sqlType);
            if (sqlTypeName != null) {
                sqlTypeNames[i] = sqlTypeName;
            }
        }

        boolean allTypesFound = !ArrayUtils.contains(sqlTypeNames, (Object)null);
        if (!allTypesFound) {
            this.LOGGER.log(Level.WARNING, "Fetching fields from Database");
            DatabaseMetaData metaData = cx.getMetaData();
            ResultSet types = metaData.getTypeInfo();

            try {
                while(types.next()) {
                    int sqlType = types.getInt("DATA_TYPE");
                    sqlTypeName = types.getString("TYPE_NAME");

                    for(int i = 0; i < sqlTypes.length; ++i) {
                        if (sqlTypeNames[i] == null && sqlType == sqlTypes[i]) {
                            sqlTypeNames[i] = sqlTypeName;
                            jdbcDataStore.getDBsqlTypesCache().putIfAbsent(sqlType, sqlTypeName);
                        }
                    }
                }
            } finally {
                jdbcDataStore.closeSafe(types);
            }
        }

        Map<Integer, String> overrides = jdbcDataStore.getSqlTypeToSqlTypeNameOverrides();

        for(int i = 0; i < sqlTypes.length; ++i) {
            String override = (String)overrides.get(sqlTypes[i]);
            if (override != null) {
                sqlTypeNames[i] = override;
            }
        }

        return sqlTypeNames;
    }

}
